/*
 * @Description:
 * @Author: Haor
 * @Date: 2022-04-06 21:07:26
 * @LastEditTime: 2022-04-08 14:28:09
 * @LastEditors: Haor
 */
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { In, Like, Repository } from "typeorm";
import * as ExcelJS from 'exceljs';
import { CreateCommonDto } from './dto/create-common.dto'
import { UpdateCommonDto } from './dto/update-common.dto'
import { BatchQueryDto } from './dto/batch-query-dict.dto'
import { Dict, DictDetail } from '@entities/common.entity'
import { HttpPageTemplate, HttpResTemplate, HttpResult } from "@libs/http.result";
import { isNumber, listGroupBy, pageWrap } from "@utils/data";
import { CreateDictDto, CreateDictValueDto, GetDictListDto } from "@dto/dict.dto";
import { BusinessException, ParamsException } from "@exceptions";
import { SnowflakeId } from "@libs/snowflake.id";
import { format } from "@utils/date";
import { CacheService } from "@cache";

@Injectable()
export class CommonService {
    constructor(
        private readonly cacheService: CacheService,

        @InjectRepository(Dict)
        private readonly dictRepository: Repository<Dict>,

        @InjectRepository(DictDetail)
        private readonly dictDetailRepository: Repository<DictDetail>,
    ) {}

    // 字典缓存Redis key
    private DICT_CACHE_KEY = 'sys_dict_list'

    create(createCommonDto: CreateCommonDto) {
        return 'This action adds a new common'
    }

    findOne(id: number) {
        return `This action returns a #${id} common`
    }

    update(id: number, updateCommonDto: UpdateCommonDto) {
        return `This action updates a #${id} common`
    }

    remove(id: number) {
        return `This action removes a #${id} common`
    }

    /**
     * 获取字典名称列表
     * @param {GetDictListDto} params
     */
    async getDictNameList(params: GetDictListDto): Promise<HttpPageTemplate> {
        const where = { isDelete: 0 }
        if (params.keyword) {
            where['description'] = Like(`%${params.keyword}%`)
        }
        // 字典列表
        const [list, total] = await this.dictRepository.findAndCount({
            select: ['id', 'name', 'description'],
            where,
            ...pageWrap(params.pageIndex, params.pageSize)
        })
        return HttpResult.pageData({ list, total })
    }

    /**
     * 获取字典列表
     */
    async getDictList(): Promise<HttpResTemplate> {
        const cacheDictList = await this.cacheService.get(this.DICT_CACHE_KEY)

        // 字典列表
        const dictList = await this.dictRepository.find({
            select: ['id', 'name'],
            where: {
                isDelete: 0
            }
        })


        if (!dictList.length) {
            return HttpResult.success({})
        }

        // 字典详情
        const dictDetailList = await this.dictDetailRepository.find({
            select: ['id', 'dictId', 'label', 'value', 'sort'],
            where: {
                dictId: In(dictList.map(d => d.id)),
                isDelete: 0
            },
        })

        // 通过id分组
        const dictResMap = listGroupBy(dictDetailList, dict => dict.dictId)

        const dictResList = this.formatDictList({
            list: dictList,
            detailList: dictDetailList,
            dictMap: dictResMap,
        })

        await this.cacheService.set(this.DICT_CACHE_KEY, dictResList) // 存入缓存中

        // 格式化，并返回结果
        return HttpResult.success(dictResList)
    }

    /**
     * 创建字典
     * @param {CreateDictDto} params
     */
    async createDict(params: CreateDictDto): Promise<HttpResTemplate> {
        // 检测是否存在
        if (await this.checkDictExistByName(params.name)) {
            throw new BusinessException('名称已存在')
        }
        const newDict = this.dictRepository.create(params)
        newDict.id = SnowflakeId.generate()
        newDict.isDelete = 0
        newDict.createTime = format(new Date())
        const saveRes = await this.dictRepository.save(newDict)
        return HttpResult.success(saveRes)
    }

    /**
     * 创建字典值
     * @param {Number} dictId 字典ID
     * @param {CreateDictValueDto} params
     */
    async createDictValue(dictId:number, params: CreateDictValueDto): Promise<HttpResTemplate> {
        const dict = await this.getDictById(dictId)
        if (!dict) {
            throw new BusinessException('字典不存在')
        }
        const dictValueList = await this.getDictValueById(dictId)
        const findData = dictValueList.find(item => (item.value === params.value || item.label === params.name))
        //验证名称，值是否重复
        if (findData) {
            throw new BusinessException('字典名称或值重复')
        }
        const newDictValue = this.dictDetailRepository.create(params)
        newDictValue.id = SnowflakeId.generate()
        newDictValue.dictId = dictId
        newDictValue.label = params.name
        newDictValue.value = params.value.toString()
        newDictValue.createTime = format(new Date())
        const saveRes = await this.dictDetailRepository.save(newDictValue)
        if (!saveRes) {
            throw new BusinessException('操作失败')
        }
        return HttpResult.success(saveRes)
    }

    /**
     * 批量获取字段列表
     */
    async getDictBatchList({ list }: BatchQueryDto): Promise<HttpResTemplate> {
        if (list.length === 0) {
            throw new ParamsException('list不能为空')
        }

        // 字典列表
        const dictList = await this.dictRepository.find({
            select: ['id', 'name'],
            where: {
                name: In([...list]),
                isDelete: 0
            },
        })

        // 如果为空
        if (!dictList || !dictList.length) {
            return HttpResult.success(
                list.reduce((res, cur) => {
                    res[cur] = null
                    return res
                }, {}),
            )
        }

        // 字典详情
        const dictDetailList = await this.dictDetailRepository.find({
            select: ['id', 'dictId', 'label', 'value', 'sort'],
            where: {
                dictId: In(dictList.map(d => d.id)),
                isDelete: 0
            },
        })

        // 通过id分组
        const dictResMap = listGroupBy(dictDetailList, (dict) => dict.dictId)

        // 格式化，并返回结果
        return HttpResult.success(
            this.formatDictList({
                list: dictList,
                detailList: dictDetailList,
                dictMap: dictResMap,
            }),
        )
    }

    /**
     * 通过字典id删除
     * @param id 字典id
     */
    async deleteDictById(id: number): Promise<HttpResTemplate> {
        const dict = await this.getDictById(id)
        if (!dict) {
            throw new BusinessException('操作错误')
        }
        const { affected } = await this.dictRepository.update(
            { id: id },
            { isDelete: 1 }
        )
        if (!affected) {
            throw new BusinessException('操作失败');
        }
        return HttpResult.success()
    }

    /**
     * 删除字典值
     * @param {Number} valueId
     */
    async deleteDictValue(valueId: number): Promise<HttpResTemplate> {
        if (!isNumber(valueId)) {
            throw new ParamsException()
        }
        const find = await this.dictDetailRepository.findOne({ where:  {id: valueId }})
        if (!find) throw new ParamsException()
        const res = await this.dictDetailRepository.update({ id: valueId }, { isDelete: 1 });
        if (!res.affected) {
            throw new BusinessException('操作异常')
        }
        return HttpResult.success()
    }

    /**
     * 导出字典Excel
     */
    async exportDict(): Promise<any> {
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('excel表格');
        // 定义表头名称和字段名
        worksheet.columns = [
            { header: 'ID', key: 'id', width: 42 },
            { header: '项目名称', key: 'name', width: 32 },
            { header: '所属区域', key: 'regionName' },
            { header: '订单编号', key: 'orderNumber' },
            { header: '管理单位', key: 'organization' },
            { header: '联系人', key: 'contact' },
            { header: '联系电话', key: 'phone', width: 12 },
            { header: '建设时间', key: 'buildAt', width: 12 },
            { header: '备注', key: 'remark' },
        ];
        const result = []; // result是通过前端传递的ids从数据库获取需要导出的信息
        worksheet.addRows(result);
        return workbook.xlsx.writeFile('second.xlsx'); // 前端接受到的数据格式为{type: 'buffer', data: []}
    }

    /**
     * 通过id查询字典
     * @param {Number} userId
     * @return {Promise}
     */
    private getDictById(dictId: number): Promise<Dict|null> {
        return new Promise(async (resolve) => {
            try {
                const dict = await this.dictRepository.findOne({
                    id: dictId,
                    isDelete: 0
                })
                resolve(dict)
            } catch (error) {
                console.log('error>>>:', error)
                resolve(null)
            }
        })
    }

    /**
     * 根据字典id查询列表
     * @param dictId
     * @private
     */
    private  getDictValueById(dictId: number): Promise<Array<DictDetail>|null> {
        return new Promise(async (resolve) => {
            try {
                const list = await this.dictDetailRepository.find({
                    where: { dictId, isDelete: 0 }
                })
                resolve(list)
            } catch (error) {
                console.log('error>>>:', error)
                resolve(null)
            }
        })
    }

    /**
     * 格式化字典列表
     * @param param0
     */
    private formatDictList({ list, detailList, dictMap }): any[] {
        return list.reduce((res, dict) => {
            const { id, name } = dict
            const detailList = Array.isArray(dictMap[id]) ? dictMap[id].map(item => {
                item.dictName = name
                return item
            }) : []
            res[name] = dictMap[id] || []
            return res
        }, {})
    }

    /**
     * 通过名称判断字典是否存在
     * @return {Promise<boolean>}
     * @private
     */
    private async checkDictExistByName(dictName) {
        const res = await this.dictRepository.findOne({ where: {name: dictName} })
        if (res) return Promise.resolve(true)
        return Promise.resolve(false)
    }
}
